/*
*	Copyright(c) 2020 lutianming email：641471957@qq.com
*
*	Sheeps may be copied only under the terms of the GNU Affero General Public License v3.0
*/

#include "LuaProtocol.h"
#include "luavms.h"

const char* main_lua = "project/lua/0/script/main.lua";
const char* config_lua = "project/lua/0/config.ini";

LuaProtocol::LuaProtocol(){
#ifdef USER_CREATE_LUAVM_INIT
	PL = create_lua_State();
	if (!PL)return;
	char cmd[64] = { 0x0 };
	snprintf(cmd, sizeof(cmd), "require \"%s\"", _SHEEPL_LUA);
	int state = luaL_dostring(PL, cmd);
	if (state != LUA_OK) {
		return;
	}
	lua_getglobal(PL, _LuavmInit);
	lua_pushinteger(PL, projectid);
	state = lua_pcall(PL, 1, 0, 0);
	if (state != LUA_OK) {
		return;
	}
#endif // USER_CREATE_LUAVM_INIT
}

LuaProtocol::~LuaProtocol(){
	if (PL) lua_close(PL); 
}

void LuaProtocol::EventStart(){
	int state = LUA_OK;
	if (!PL) {
		PL = create_lua_State();
		if (!PL) {
			PlayStop(this, "%s:%d luavm init error", __func__, __LINE__);
			return;
		}
	}
#ifndef USER_CREATE_LUAVM_INIT
	if (!env_inited) {
		char cmd[64] = { 0x0 };
		snprintf(cmd, sizeof(cmd), "require \"%s\"", _SHEEPL_LUA);
		state = luaL_dostring(PL, cmd);
		if (state != LUA_OK) {
			sheeps_report(PL, state, this, true, __func__, __LINE__);
			return;
		}
		lua_getglobal(PL, _LuavmInit);
		lua_pushinteger(PL, projectid);
		state = lua_pcall(PL, 1, 0, 0);
		if (state != LUA_OK) {
			sheeps_report(PL, state, this, true, __func__, __LINE__);
			return;
		}
		env_inited = 1;
	}
#endif // USER_CREATE_LUAVM_INIT
	if (init_main_script(PL, this)) {
		PlayStop(this, "%s:%d luavm run main script error", __func__, __LINE__);
		return;
	}

	lua_getglobal(PL, __func__);
	lua_getglobal(PL, _Task);
	state = lua_pcall(PL, 1, 0, 0);
	if (state != LUA_OK){
		sheeps_report(PL, state, this, true, __func__, __LINE__);
	}
}

void LuaProtocol::EventConnectMade(HSOCKET hsock){
	if (PL) {
		if (hsock->user_protocol == USER_PROTOCOL_NONE) {
			lua_getglobal(PL, __func__);
		}
		else {  //USER_PROTOCOL_HANDLE
			lua_rawgeti(PL, LUA_REGISTRYINDEX, hsock->user_val);
			lua_pushstring(PL, __func__);
			lua_gettable(PL, -2);
		}
		lua_pushlightuserdata(PL, hsock);
		int state = lua_pcall(PL, 1, 0, 0);
		if (state != LUA_OK){
			sheeps_report(PL, state, this, true, __func__, __LINE__);
		}
	}
}

void LuaProtocol::EventConnectFailed(HSOCKET hsock, int err){
	if (PL) {
		if (hsock->user_protocol == USER_PROTOCOL_NONE) {
			lua_getglobal(PL, __func__);
		}
		else {  //USER_PROTOCOL_HANDLE
			lua_rawgeti(PL, LUA_REGISTRYINDEX, hsock->user_val);
			lua_pushstring(PL, __func__);
			lua_gettable(PL, -2);
		}
		lua_pushlightuserdata(PL, hsock);
		lua_pushinteger(PL, err);
		int state = lua_pcall(PL, 2, 0, 0);
		if (state != LUA_OK){
			sheeps_report(PL, state, this, true, __func__, __LINE__);
		}
	}
}

void LuaProtocol::EventConnectClosed(HSOCKET hsock, int err){
	if (PL) {
		if (hsock->user_protocol == USER_PROTOCOL_NONE) {
			lua_getglobal(PL, __func__);
		}
		else {  //USER_PROTOCOL_HANDLE
			int ref = int(hsock->user_val);
			lua_rawgeti(PL, LUA_REGISTRYINDEX, ref);
			lua_pushstring(PL, __func__);
			lua_gettable(PL, -2);

			luaL_unref(PL, LUA_REGISTRYINDEX, ref);
		}
		lua_pushlightuserdata(PL, hsock);
		lua_pushinteger(PL, err);
		int state = lua_pcall(PL, 2, 0, 0);
		if (state != LUA_OK){
			sheeps_report(PL, state, this, true, __func__, __LINE__);
		}
	}
}

void LuaProtocol::EventConnectRecved(HSOCKET hsock, const char* data, int len){
	if (PL) {
		if (hsock->user_protocol == USER_PROTOCOL_NONE) {
			lua_getglobal(PL, __func__);
		}
		else {  //USER_PROTOCOL_HANDLE
			lua_rawgeti(PL, LUA_REGISTRYINDEX, hsock->user_val);
			lua_pushstring(PL, __func__);
			lua_gettable(PL, -2);
		}
		lua_pushlightuserdata(PL, hsock);
		lua_pushlstring(PL, data, len);
		HsocketPopBuf(hsock, len);
		int state = lua_pcall(PL, 2, 0, 0);
		if (state != LUA_OK){
			sheeps_report(PL, state, this, true, __func__, __LINE__);
		}
	}
}

void LuaProtocol::EventConnectOpen(const char* ip, int port, PROTOCOL protocol){
	lua_getglobal(PL, __func__);
	lua_pushstring(PL, ip);
	lua_pushinteger(PL, port);
	lua_pushinteger(PL, protocol);
	int state = lua_pcall(PL, 3, 0, 0);
	if (state != LUA_OK){
		sheeps_report(PL, state, this, true, __func__, __LINE__);
	}
}

void LuaProtocol::EventConnectClose(const char* ip, int port, PROTOCOL protocol){
	lua_getglobal(PL, __func__);
	lua_pushstring(PL, ip);
	lua_pushinteger(PL, port);
	lua_pushinteger(PL, protocol);
	int state = lua_pcall(PL, 3, 0, 0);
	if (state != LUA_OK){
		sheeps_report(PL, state, this, true, __func__, __LINE__);
	}
}

void LuaProtocol::EventConnectSend(const char* ip, int port, const char* content, int clen, PROTOCOL protocol){
	lua_getglobal(PL, __func__);
	lua_pushstring(PL, ip);
	lua_pushinteger(PL, port);
	lua_pushlstring(PL, content, clen);
	lua_pushinteger(PL, protocol);
	int state = lua_pcall(PL, 4, 0, 0);
	if (state != LUA_OK){
		sheeps_report(PL, state, this, true, __func__, __LINE__);
	}
}

void LuaProtocol::EventTimeOut(){
	lua_getglobal(PL, __func__);
	int state = lua_pcall(PL, 0, 0, 0);
	if (state != LUA_OK){
		sheeps_report(PL, state, this, true, __func__, __LINE__);
		return;
	}

	//call regist function
	lua_getfield(PL, LUA_REGISTRYINDEX, _FunctionCall);
	lua_pushnil(PL);
	while (lua_next(PL, -2) != 0) {
		lua_pushstring(PL, "plen");
		lua_gettable(PL, -2);
		int count = (int)lua_tointeger(PL, -1);
		lua_pop(PL, 1);

		lua_pushstring(PL, "function");
		lua_gettable(PL, -2);

		lua_pushstring(PL, "params");
		lua_gettable(PL, -3);
		for (int i = 1; i <= count; i++) {
			lua_pushinteger(PL, i);
			lua_gettable(PL, -1 - i);
		}
		lua_remove(PL, -(count + 1));

		int state = lua_pcall(PL, count, 0, 0);
		if (state != LUA_OK) {
			sheeps_report(PL, state, this, true, __func__, __LINE__);
			return;
		}
		lua_pop(PL, 1);
	}
	lua_pop(PL, 1);

	if (check_time_tick(&this->luaLastGc, 10)){
		lua_gc(PL, LUA_GCCOLLECT, 0);
	}
}

void LuaProtocol::EventStop(){
	if (PL){
		lua_sethook(PL, NULL, 0, 0);
		lua_getglobal(PL, __func__);
		lua_pushstring(PL, this->stop_reason);
		int state = lua_pcall(PL, 1, 0, 0);
		if (state != LUA_OK){
			sheeps_report(PL, state, this, false, __func__, __LINE__);
		}
		reset_luavm(PL);
	}
}

void LuaProtocol::EventStopSignal() {
	if (PL) {
		lua_sethook(PL, stop_signal_hook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, 1);
	}
}
